# _*_ coding:utf-8 _*_
# @Time  : 2022.12.13
# @Author: zizlee

import pathlib
import math
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from settings import FILE_STORAGE, STATIC_HOST
from utils import datetime_utils

# Agg, is a non-interactive backend that can only write to files.
matplotlib.use('agg')  # 避免警告： UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.
plt.rcParams['font.sans-serif'] = ['SimHei']  # 显示中文
plt.rcParams["axes.unicode_minus"] = False


# 有关交易统计表的分析
class ExchangeStatistician(object):
    TEMP_FOLDER = pathlib.Path(FILE_STORAGE).joinpath('sysTemp/exDoctor')

    def __init__(self, exchange_data_list):
        self.exchange_df = pd.DataFrame(exchange_data_list)

    # 生成板块盈亏分析图形-文字
    def create_block_profit_image(self, filename_prefix, fig_size, xtick_count):
        block_profit_data = self.block_profit()
        month_list = list(set([m['month'] for m in block_profit_data['month_data']]))
        block_list = list(set([b['block_name'] for b in block_profit_data['block_data']]))
        month_list.sort()
        # 板块逐月盈亏柱形图
        # m  [05,06,07]  横轴
        # b1 [10,20,30]  # 板块1
        # b2 [10,20,30]  # 板块2
        # ...            # ...
        block_values = [[0 for _ in month_list] for _ in block_list]
        for item in block_profit_data['month_data']:
            # 看是哪个block
            bi = None
            for i, b_name in enumerate(block_list):
                if b_name == item['block_name']:
                    bi = i
                    break
            # 看是哪个月的
            mi = None
            for j, m_name in enumerate(month_list):
                if m_name == item['month']:
                    mi = j
                    break
            if bi is not None and mi is not None:
                block_values[bi][mi] = item['profit_close']

        x_pos = np.arange(len(month_list))  # the label locations
        width = 0.15  # the width of the bars
        fig, ax = plt.subplots(figsize=fig_size)
        block_count = len(block_list)
        offset = block_count // 2
        for i, blk in enumerate(block_list):
            if i == offset:
                rect = ax.bar(x_pos, block_values[i], width, label=blk, zorder=10)
            else:
                rect = ax.bar(x_pos - (offset - i) * width, block_values[i], width, label=blk, zorder=10)
            # ax.bar_label(rect, padding=2)  #显示数字的
        ax.set_ylabel('盈亏')
        ax.set_title('板块每月盈亏')
        ax.legend(frameon=False)
        # 设置横轴
        x_labels = month_list
        x_ticks = self.get_x_ticks(x_labels, xtick_count)
        plt.xticks([i for i in range(len(x_labels))], x_ticks, rotation=30, fontsize=9)
        plt.title('板块每月盈亏')
        for loc_axis in ['left', 'top', 'right']:
            ax.spines[loc_axis].set_visible(False)
        ax.grid(axis='y', zorder=0)
        bp_img_month = self.TEMP_FOLDER.joinpath(f'{filename_prefix}_block_profit_month.png')
        plt.savefig(bp_img_month)
        bp_month_txt = self.TEMP_FOLDER.joinpath(f'{filename_prefix}_block_profit_month.txt')
        with open(bp_month_txt, 'w', encoding='utf8') as f:
            f.write(block_profit_data['month_description'])

        # 板块盈亏图
        block_name = []
        block_value = []
        for item in block_profit_data['block_data']:
            block_name.append(item['block_name'])
            block_value.append(item['profit_close'])

        fig, ax1 = plt.subplots(figsize=fig_size)
        plt.xticks([i for i in range(len(block_name))], block_name, rotation=30, fontsize=9)
        ax1.bar(block_name, block_value, label='板块盈亏', zorder=10)
        ax1.set_xlabel('板块')
        ax1.set_ylabel('盈亏')
        plt.title('板块盈亏')
        for loc_axis in ['left', 'top', 'right']:
            ax1.spines[loc_axis].set_visible(False)
        ax1.grid(axis='y', zorder=0)
        bp_img = self.TEMP_FOLDER.joinpath(f'{filename_prefix}_block_profit.png')
        plt.savefig(bp_img)
        bp_txt = self.TEMP_FOLDER.joinpath(f'{filename_prefix}_block_profit.txt')
        with open(bp_txt, 'w', encoding='utf8') as f:
            f.write(block_profit_data['block_description'])

        bp_img_file = str(bp_img).replace('\\', '/').replace(FILE_STORAGE, '')
        bp_img_url = STATIC_HOST + bp_img_file
        bp_img_month_file = str(bp_img_month).replace('\\', '/').replace(FILE_STORAGE, '')
        bp_img_month_url = STATIC_HOST + bp_img_month_file
        return {
            'bp_img_month': bp_img_month_file,
            'bp_img_month_url': bp_img_month_url,
            'bp_txt_month': block_profit_data['month_description'],
            'bp_img': bp_img_file,
            'bp_img_url': bp_img_url,
            'bp_txt': block_profit_data['block_description']
        }

    # 生成品种持仓结构图形-文字
    def create_holding_struct(self, filename_prefix, fig_size, xtick_count):
        variety_bail_data = self.variety_holding_bail()
        variety_list = []
        variety_bail = []
        for item in variety_bail_data['data']:
            variety_list.append(item['variety_name'])
            variety_bail.append(item['bail'])
        fig, ax1 = plt.subplots(figsize=fig_size)
        x_labels = variety_list
        x_ticks = self.get_x_ticks(x_labels, xtick_count)
        plt.xticks([i for i in range(len(x_labels))], x_ticks, rotation=30, fontsize=9)
        ax1.bar(x_labels, variety_bail, label='持仓保证金', zorder=10)
        ax1.set_xlabel('品种')
        ax1.set_ylabel('保证金')
        plt.title('品种持仓保证金')
        for loc_axis in ['left', 'top', 'right']:
            ax1.spines[loc_axis].set_visible(False)
        ax1.grid(axis='y', zorder=0)
        plt.savefig(f'temp/images/{filename_prefix}_variety_bail.png')
        with open(f'temp/txt/{filename_prefix}_variety_bail.txt', 'w', encoding='utf8') as f:
            f.write(variety_bail_data['description'])

    # 生成品种成交结构图形-文字
    def create_exchange_struct(self, filename_prefix, fig_size, xtick_count):
        variety_data = self.variety_exchange_struct()
        variety_list = []
        variety_ex_amount = []
        for item in variety_data['data']:
            variety_list.append(item['variety_name'])
            variety_ex_amount.append(round(item['amount_ex'] / 10000, 2))  # 万元
        fig, ax1 = plt.subplots(figsize=fig_size)
        x_labels = variety_list
        x_ticks = self.get_x_ticks(x_labels, xtick_count)
        plt.xticks([i for i in range(len(x_labels))], x_ticks, rotation=30, fontsize=9)
        ax1.bar(x_labels, variety_ex_amount, label='成交额', zorder=10)
        ax1.set_xlabel('品种')
        ax1.set_ylabel('成交额(万元)')
        plt.title('品种成交结构')
        plt.ticklabel_format(style='plain', axis='y')
        for loc_axis in ['left', 'top', 'right']:
            ax1.spines[loc_axis].set_visible(False)
        ax1.grid(axis='y', zorder=0)
        img_file = self.TEMP_FOLDER.joinpath(f'{filename_prefix}_variety_ex_amount_struct.png')
        plt.savefig(img_file)
        txt_file = self.TEMP_FOLDER.joinpath(f'{filename_prefix}_variety_ex_amount_struct.txt')
        with open(txt_file, 'w', encoding='utf8') as f:
            f.write(variety_data['description'])
        img_filename = str(img_file).replace('\\', '/').replace(FILE_STORAGE, '')
        img_url = STATIC_HOST + img_filename
        return {
            'txt': variety_data['description'],
            'img_file': img_filename,
            'img_url': img_url
        }

    # 板块盈亏分析
    def block_profit(self):
        df = self.exchange_df.copy()
        # 板块逐月盈亏
        df['month'] = df['ex_date'].apply(lambda x: datetime_utils.auth_yyyymmdd(str(x), tf='%Y%m'))
        month_df = df.groupby(by=['month', 'block_name'], as_index=False)['profit_close'].sum().round(2)
        # 板块盈亏
        block_df = df.groupby(by='block_name', as_index=False)['profit_close'].sum().round(2)
        block_df.sort_values(by='profit_close', ascending=False, inplace=True)

        min_date = datetime_utils.auth_yyyymmdd(df['ex_date'].min(), tf='%Y.%m.%d')
        max_date = datetime_utils.auth_yyyymmdd(df['ex_date'].max(), tf='%Y.%m.%d')
        # ------------计算月盈亏状态开始-------
        # 月总盈亏数
        month_total_gain = month_df[month_df['profit_close'] > 0]['profit_close'].sum()  # 月盈利总数
        month_total_loss = month_df[month_df['profit_close'] < 0]['profit_close'].sum()  # 月亏损总数
        # 月盈利
        month_gain_df = month_df[month_df['profit_close'] > 0]
        if month_gain_df.empty:
            month_highest_gain = {'month': '无', 'profit_close': 0}
            month_highest_gain_rate = 0
        else:
            month_gain_df = month_gain_df.groupby(by='month', as_index=False)['profit_close'].sum()
            month_gain_df.sort_values(by='profit_close', ascending=False, inplace=True)
            month_highest_gain = month_gain_df.head(1).to_dict(orient='records')[0]
            month_highest_gain_rate = month_highest_gain['profit_close'] / month_total_gain  # 最大盈利月占总盈利比例
        # 月亏损
        month_loss_df = month_df[month_df['profit_close'] < 0]
        if month_loss_df.empty:
            month_highest_loss = {'month': '无', 'profit_close': 0}
            month_highest_loss_rate = 0
        else:
            month_loss_df = month_loss_df.groupby(by='month', as_index=False)['profit_close'].sum()
            month_loss_df.sort_values(by='profit_close', ascending=True, inplace=True)
            month_highest_loss = month_loss_df.head(1).to_dict(orient='records')[0]
            month_highest_loss_rate = month_highest_loss['profit_close'] / month_total_loss  # 最大亏损月占总盈利比例
        # ------------计算月盈亏状态结束-------

        # ------------计算板块盈亏状态开始-------
        # 板块总盈利数
        block_total_gain = block_df[block_df['profit_close'] > 0]['profit_close'].sum()  # 板块盈利总数
        block_total_loss = block_df[block_df['profit_close'] < 0]['profit_close'].sum()  # 板块亏损总数
        block_highest_gain = block_df.head(1).to_dict(orient='records')[0]
        if block_highest_gain['profit_close'] < 0:
            block_highest_gain = {'block_name': '无', 'profit_close': 0}
            block_highest_gain_rate = 0
        else:
            block_highest_gain_rate = block_highest_gain['profit_close'] / block_total_gain

        block_highest_loss = block_df.tail(1).to_dict(orient='records')[0]
        if block_highest_loss['profit_close'] > 0:
            block_highest_loss = {'block_name': '无', 'profit_close': 0}
            block_highest_loss_rate = 0
        else:
            block_highest_loss_rate = block_highest_loss['profit_close'] / block_total_loss
        # ------------计算板块盈亏状态结束-------

        # 生成描述
        blocks = block_df['block_name'].tolist()
        description = f"{min_date}至{max_date}，交易涉及{'、'.join(blocks)}等{len(blocks)}个板块，"

        month_dept = description + f"其中盈利最多的月份是{month_highest_gain['month']}，占总盈利的{round(month_highest_gain_rate * 100, 2)}%。亏损最多的月份是{month_highest_loss['month']}，占总亏损的{round(month_highest_loss_rate * 100, 2)}%。"
        block_dept = description + f"其中盈利最多的板块是{block_highest_gain['block_name']}，占总盈利的{round(block_highest_gain_rate * 100, 2)}%；亏损最多的板块是{block_highest_loss['block_name']}，占总亏损的{round(block_highest_loss_rate * 100, 2)}%。"

        return {
            'month_data': month_df.to_dict(orient='records'),
            'block_data': block_df.to_dict(orient='records'),
            'month_description': month_dept,
            'block_description': block_dept
        }

    # 品种保证金结构
    def variety_holding_bail(self):
        df = self.exchange_df.copy()
        bail_df = df.groupby(by='variety_name', as_index=False)['deposit_fn'].sum().round(2)
        bail_df = bail_df[bail_df['deposit_fn'] > 0]
        bail_df.rename(columns={'deposit_fn': 'bail'}, inplace=True)
        bail_df.sort_values(by='bail', inplace=True, ascending=False)
        bail_list = bail_df.to_dict(orient='records')
        # 交易周期
        start = datetime_utils.auth_yyyymmdd(df['ex_date'].min(), obj=True)
        end = datetime_utils.auth_yyyymmdd(df['ex_date'].max(), obj=True)
        variety_list = bail_df['variety_name'].tolist()
        # 计算占比
        total_bail = bail_df['bail'].sum()
        max_bail = bail_df.head(1).to_dict(orient='records')[0]
        max_bail_ratio = max_bail['bail'] / total_bail
        min_bail = bail_df.tail(1).to_dict(orient='records')[0]
        min_bai_ratio = min_bail['bail'] / total_bail
        # 生成描述文字
        description_text = f"报告期{start.strftime('%Y.%m.%d')}至{end.strftime('%Y.%m.%d')}，"
        description_text += f"交易的品种有{'、'.join(variety_list[:5])}等{len(variety_list)}个。"
        description_text += f"其中，保证金累计最大的是{max_bail['variety_name']}，占总累计的{round(max_bail_ratio * 100, 2)}%，"
        description_text += f"最小的是{min_bail['variety_name']}，占总累计的{round(min_bai_ratio * 100, 2)}%。"

        return {
            'data': bail_list,
            'description': description_text
        }

    # 交易结构：各品种的成交额
    def variety_exchange_struct(self):
        df = self.exchange_df.copy()
        struct_df = df.groupby(by='variety_name', as_index=False)['amount_ex'].sum().round(2)
        # 交易周期
        start = datetime_utils.auth_yyyymmdd(df['ex_date'].min(), obj=True)
        end = datetime_utils.auth_yyyymmdd(df['ex_date'].max(), obj=True)
        variety_list = struct_df['variety_name'].tolist()
        struct_df.sort_values(by='amount_ex', inplace=True, ascending=False)
        ex_list = struct_df.to_dict(orient='records')
        # 计算占比
        total_amount = struct_df['amount_ex'].sum()
        max_amount = struct_df.head(1).to_dict(orient='records')[0]
        max_amount_ratio = max_amount['amount_ex'] / total_amount
        min_amount = struct_df.tail(1).to_dict(orient='records')[0]
        min_amount_ratio = min_amount['amount_ex'] / total_amount
        # 生成描述文字
        description_text = f"报告期{start.strftime('%Y.%m.%d')}至{end.strftime('%Y.%m.%d')}，"
        description_text += f"交易的品种有{'、'.join(variety_list[:5])}等{len(variety_list)}个。"
        description_text += f"其中，成交额累计最大的是{max_amount['variety_name']}，占总累计的{round(max_amount_ratio * 100, 2)}%，"
        description_text += f"最小的是{min_amount['variety_name']}，占总累计的{round(min_amount_ratio * 100, 2)}%。"
        return {
            'data': ex_list,
            'description': description_text
        }

    @staticmethod
    def get_x_ticks(x_labels, xtick_count):
        gap_num = math.ceil(len(x_labels) / xtick_count)
        x_ticks = []
        for i, x in enumerate(x_labels):
            x_ticks.append(x) if i % gap_num == 0 else x_ticks.append('')
        x_ticks[-1] = x_labels[-1]
        return x_ticks

